package com.gitee.l0km.thrift.swift2thrift.generator.plugin;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;

import com.facebook.swift.generator.swift2thrift.Swift2ThriftGeneratorConfig;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.gitee.l0km.com4j.base.MiscellaneousUtils;
import com.gitee.l0km.thrift.swift2thrift.generator.Swift2ThriftGenerator;
import com.gitee.l0km.xthrift.base.metadata.DecoratorThriftFieldMetadata;

/**
 * maven 插件<br>
 * 从swift java class 类生成thrift IDL文件<br>
 * Swift2Thrift generates '.thrift' files from Swift-annotated Java files. 
 * The generated '.thrift' files can be fed to a Thrift compiler to generate client and/or server code for other languages.<br>
 * for more detail, see also https://gitee.com/l0km/idl-generator
 * @author guyadong
 *
 */
@Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES,requiresDependencyResolution=ResolutionScope.COMPILE)
public class Swift2ThriftMojo extends AbstractMojo {
	@Parameter(defaultValue = "${project}", readonly = true, required = true)
	protected MavenProject project;
	/**
	 * Swift-annotated Java class name list, be used for IDL generation 
	 */
	@Parameter(required=true,property = "swift2thrift.swiftClassNames")
    private List<String>  swiftClassNames;
	/**
	 * Thrift IDL output file, defaults to stdout
	 */
	@Parameter(defaultValue = "${project.build.directory}/generated-sources/thrift.thrift",property = "swift2thrift.out")
	private File out = null;
	
	/**
	 * Map of external type or service to include file
	 */
	@Parameter(property = "swift2thrift.include")
	private Map<String,String> include =  ImmutableMap.of();
	/**
	 * Show verbose messages
	 */
	@Parameter(property = "swift2thrift.verbose")
    private boolean verbose = false;
	/**
	 * Default package for unqualified classes
	 */
	@Parameter(required=true,property = "swift2thrift.defaultPackage")
	private String defaultPackage = "";
	/**
	 * Namespace for a particular language to include
	 */
	@Parameter(property = "swift2thrift.namespace")
    private Map<String,String> namespace= ImmutableMap.of();
	/**
      * Allow input classes to reside in different packages.
      * The value of this flag defines the generated java.swift namespace.
      * Note that Swift classes generated from the resultant Thrift file will all reside in one Java package
	  */
	@Parameter(property = "swift2thrift.allowMultiplePackages")
	private String allowMultiplePackages;
    /**
     * Generate all types transitively reachable from the specified types and services
     */
    @Parameter(property = "swift2thrift.recursive")
    private boolean recursive = false;
    /**
     * Generate optional field and parameter  from the  wrap type of primitive type(such as Integer,Long...)
     */
    @Parameter(property = "swift2thrift.primitiveOptional")
    private boolean primitiveOptional = true;
    /**
     * Generate optional field and parameter  from all Object type of not primitive type
     */
    @Parameter
    private boolean objectOptional = false;    
    /**
     * Method names list  separated by comma or semicolon that excluded from interface
     */
    @Parameter(property = "swift2thrift.excludeMethods")
    private String excludeMethods = "";
    /**
     * Method names list  separated by comma or semicolon that excluded from interface
     */
    @Parameter(property = "swift2thrift.includeMethods")
    private String includeMethods = "";
    /**
     * sort declared methods by swift2thrift.includeMethods
     */
    @Parameter(property = "swift2thrift.sortByInclude")
    private boolean sortByInclude = false;
    /**
     * Generate eRPC IDL file,default: false(generate thrift IDL)
     */
    @Parameter(alias="erpc", property = "swift2thrift.erpc")
    private boolean generateErpcIDL = false;
    /**
     * name of eRPC program
     */
    @Parameter( property = "swift2thrift.erpc.programName")
    private String programName = "";
    /**
     * prefix of port method for eRPC,default empty
     */
    @Parameter(property = "swift2thrift.erpc.PortPrefix")
    private String portPrefix;
    /**
     * path list of source separated by semicolon for reading comments by javadoc
     */
    @Parameter(property = "swift2thrift.sourcePath")
    private String sourcePath;
    /**
     * Specifies the paths where javadoc will look for referenced classes (.class files),splited with [:] On UNIX systems, 
     * splited with [;]  on Microsoft Windows systems<br>
     * Ignored since 1.24
     */
    @Parameter(property = "swift2thrift.classPath")
    private String classPath;
    /**
     * use comments read by javadoc as documentation
     */
    @Parameter(property = "swift2thrift.javadocEnable")
    private boolean javadocEnable;
    /**
     * default value for annotation @max_length
     */
    @Parameter(property = "swift2thrift.erpc.defaultMaxLength")
    private int defaultMaxLength=256;
    /**
     * max length for output error message buffer of  each service port 
     */
    @Parameter(property = "swift2thrift.erpc.errmsgMaxLength")
    private int errmsgMaxLength=256;
    /**
     * generate for eRPC proxy service,default:true
     */
    @Parameter(property = "swift2thrift.erpc.proxy")
    private boolean erpcProxy = true;
    /**
     * exclude fields of class name 
     */
    @Parameter(property = "swift2thrift.erpc.excludeFields")
    public Map<String, String> excludeFields;
	public Swift2ThriftMojo() {
	}

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		getLog().info("Swift2thrift plugin generate IDL file(生成Thrift接口定义文件)");
		getLog().info("swiftClassNames = "+ swiftClassNames);
		getLog().info("outputFile = " + out.getAbsolutePath());
		getLog().info("defaultPackage = "+ defaultPackage);
		getLog().info("allowMultiplePackages = " + allowMultiplePackages);
        getLog().info("include = " + include);
        getLog().info("namespace = " + namespace);
        getLog().info("primitiveOptional = "+ primitiveOptional);
        getLog().info("objectOptional = "+ objectOptional);
        getLog().info("generateErpcIDL = "+ generateErpcIDL);
        if(generateErpcIDL){
        	getLog().info("\teEPC programName = "+ programName);
        	getLog().info("\teEPC portPrefix = "+ portPrefix);
        }
        String formated = Joiner.on(",\n\t\t").join(MiscellaneousUtils.elementsOf(excludeMethods));
        getLog().info("excludeMethods = "+ formated);
        getLog().info("recursive = "+ recursive);
        getLog().info("verbose = "+ verbose);
        getLog().info("sourcepath = "+ sourcePath);
        classPath = buildClasspath();
        getLog().info("classpath = "+ classPath);
        getLog().info("javadocEnable = "+ javadocEnable);
        getLog().info("defaultMaxLength = "+ defaultMaxLength);
        getLog().info("erpcProxy = "+ erpcProxy);
        getLog().info("excludeFields = "+ excludeFields);
        Swift2ThriftGeneratorConfig.Builder configBuilder = Swift2ThriftGeneratorConfig.builder()
                .outputFile(out)
                .includeMap(include)
                .verbose(verbose)
                .defaultPackage(defaultPackage)
                .namespaceMap(namespace)
                .allowMultiplePackages(allowMultiplePackages)
                .recursive(recursive);
        // 将 primitiveOptional参数传递给自定义对象
        DecoratorThriftFieldMetadata.setPrimitiveOptional(primitiveOptional);
        // 将 objectOptional参数传递给自定义对象
        DecoratorThriftFieldMetadata.setObjectOptional(objectOptional);
        try {
        	// 开始生成
			new Swift2ThriftGenerator(configBuilder.build())
				.setExcludeMethods(excludeMethods)
				.setIncludeMethods(includeMethods)
				.setSortByInclude(sortByInclude)
				.setGenerateErpcIDL(generateErpcIDL)
				.setProgramName(programName)
				.setPortPrefix(portPrefix)
	        	.setSourcepath(sourcePath)
	        	.setClasspath(classPath)
	        	.setParentClassLoader(getClass().getClassLoader())
	        	.setJavadocEnable(javadocEnable)
	        	.setDefaultMaxLength(defaultMaxLength)
	        	.setErrmsgMaxLength(errmsgMaxLength)
	        	.setErpcProxy(erpcProxy)
	        	.setExcludeFields(excludeFields)
				.parse(swiftClassNames);
		} catch (IOException e) {
			throw new MojoFailureException(e.getMessage(),e);
		}
	}
	/**
	 * 返回当前项目的依赖库列表<br>
	 * 从{@link MavenProject#getArtifacts()}读取所有依赖库，
	 * 得到以路径分割符分割的依赖库列表字符串，包括当前项目的编译路径
	 */
	private String buildClasspath() {
		String classpath = project.getArtifacts().stream()
				.map(artifact -> artifact.getFile().getAbsolutePath())
				.filter(file -> file != null)
				.collect(Collectors.joining(File.pathSeparator));
		/** 获取输出目录 */
		return classpath + File.pathSeparator + project.getBuild().getOutputDirectory();
	}
}
